#include <stdio.h>
#include <allegro.h>

#include "Common.h"
#include "String.h"
#include "Map.h"
#include "Worker.h"
#include "Object.h"

Map*  map;
Mutex mapMutex;

Mutex workerMutex;
Mutex objectMutex;

void LoadMap(FILE* file)
{
    int i, j, zCount, zData;

    map = (Map*)malloc(sizeof(Map));

    map->width = fgetc(file);
    map->height = fgetc(file);

    map->data = (unsigned char*)malloc(map->width * map->height * sizeof(short));
    for (i = 0; i < map->width * map->height; i++)
    {
        map->data[i] = fgetc(file);
        if (map->data[i] == 0xFE)
        {
            zCount = fgetc(file);
            zCount |= fgetc(file) << 8;
            zData = fgetc(file);
            for (j = i; j < i + zCount && j < map->width * map->height; j++)
                map->data[j] = zData;
            i = j - 1;
        }
    }

    map->state = (char*)malloc(map->width * map->height * sizeof(short));
    for (i = 0; i < map->width * map->height; i++)
    {
        switch (map->data[i] >> 7)
        {
            case MAP_TYPE_FLOOR: map->state[i] = MAP_STATE_FREE; break;
            case MAP_TYPE_WALL: map->state[i] = MAP_STATE_WALL; break;
        }
    }

    map->layers[0] = NULL;
    map->layers[1] = NULL;

    GenerateMapBmp(map);
}

void GenerateMapBmp(Map* m)
{
    int i;

    Lock(mapMutex);

    if (m->layers[0])
        destroy_bitmap(m->layers[0]);
    m->layers[0] = create_bitmap(m->width * 16, (m->height + 1) * 16);
	clear_to_color(m->layers[0], 0xFF00FF);

    if (m->layers[1])
        destroy_bitmap(m->layers[1]);
    m->layers[1] = create_bitmap(m->width * 16, (m->height + 3) * 16);
    clear_to_color(m->layers[1], 0xFF00FF);

    // On dessine la map de base
    for (i = 0; i < m->width * m->height; i++)
        if (m->data[i] >> 7 == MAP_TYPE_FLOOR)
            blit(GetFloorTextureBmp(), m->layers[0], (m->data[i] & 15) << 4, m->data[i] & 112, (i % m->width) << 4, (i / m->width) << 4, 16, 16);

    // On dessine les murs
    for (i = 0; i < m->width * m->height; i++)
    {
        int texX, texY, dstX, dstY, curX, curY;
		curX = i % m->width;
		curY = i / m->width;
        dstX = (i % m->width) << 4;
        dstY = (i / m->width) << 4;
        if (m->data[i] >> 7 == MAP_TYPE_WALL)
        {
            if ((curX > 0 && m->state[i - 1]) || (curX < m->width - 1 && m->state[i + 1] == MAP_STATE_WALL))
            {
                if (curY < m->height - 1 && m->state[i + m->width] == MAP_STATE_WALL)
                {
                    masked_blit(GetWallBmp(), m->layers[0], 4 << 4, 0, dstX, dstY, 16, 16);      // 4 2
                    masked_blit(GetWallBmp(), m->layers[0], 3 << 4, 0, dstX, dstY + 16, 16, 16); // 4 3
                    masked_blit(GetWallBmp(), m->layers[1], 6 << 4, 0, dstX, dstY + 16, 16, 16); // 6 0
                    masked_blit(GetWallBmp(), m->layers[1], 8 << 4, 0, dstX, dstY, 16, 16);      // 5 4
                }
                else if ((curY > 0 && m->state[i - m->width] == MAP_STATE_WALL) || !curX || !m->state[i - 1] || curX == m->width - 1 || !m->state[i + 1])
                {
                    masked_blit(GetWallBmp(), m->layers[0], 4 << 4, 0, dstX, dstY, 16, 16);
                    masked_blit(GetWallBmp(), m->layers[0], 3 << 4, 0, dstX, dstY + 16, 16, 16);
                    masked_blit(GetWallBmp(), m->layers[1], 5 << 4, 0, dstX, dstY + 16, 16, 16); // 4 1
                    masked_blit(GetWallBmp(), m->layers[1], 8 << 4, 0, dstX, dstY, 16, 16);
                }
                else
                {
                    masked_blit(GetWallBmp(), m->layers[0], 0 << 4, 0, dstX, dstY, 16, 16); // 0 1
                    masked_blit(GetWallBmp(), m->layers[1], 1 << 4, 0, dstX, dstY + 16, 16, 16); // 0 0
                    masked_blit(GetWallBmp(), m->layers[1], 2 << 4, 0, dstX, dstY, 16, 16);
                }
            }
            else if ((curY < m->height - 1 && m->state[i + m->width] == MAP_STATE_WALL) && (curY > 0 && m->state[i - m->width] == MAP_STATE_WALL))
            {
                masked_blit(GetWallBmp(), m->layers[0], 4 << 4, 0, dstX, dstY, 16, 16);
                masked_blit(GetWallBmp(), m->layers[1], 9 << 4, 0, dstX, dstY + 16, 16, 16); // 6 1
            }
            else if (curY < m->height - 1 && m->state[i + m->width] == MAP_STATE_WALL)
            {
                masked_blit(GetWallBmp(), m->layers[0], 4 << 4, 0, dstX, dstY, 16, 16);
                masked_blit(GetWallBmp(), m->layers[0], 3 << 4, 0, dstX, dstY + 16, 16, 16);
                masked_blit(GetWallBmp(), m->layers[1], 6 << 4, 0, dstX, dstY + 16, 16, 16);
                masked_blit(GetWallBmp(), m->layers[1], 8 << 4, 0, dstX, dstY, 16, 16);
            }
            else
            {
                masked_blit(GetWallBmp(), m->layers[0], 4 << 4, 0, dstX, dstY, 16, 16);
                masked_blit(GetWallBmp(), m->layers[0], 3 << 4, 0, dstX, dstY + 16, 16, 16);
                masked_blit(GetWallBmp(), m->layers[1], 5 << 4, 0, dstX, dstY + 16, 16, 16);
                masked_blit(GetWallBmp(), m->layers[1], 8 << 4, 0, dstX, dstY, 16, 16);
            }
        }
    }

    UnLock(mapMutex);
}

COORD GetRandomFreePosition()
{
    COORD coord;
    do
    {
        coord.X = rand() % map->width;
        coord.Y = rand() % map->height;
    }
    while (map->state[coord.Y * map->width + coord.X]);
    return coord;
}

int GetMapIndexByXY(int x, int y)
{
    return y * map->width + x;
}
int GetMapIndexByCoord(COORD coord)
{
    return coord.Y * map->width + coord.X;
}

int GetMapFreeSpace(int direction)
{
    int x, y;
    bool b = true;

    switch (direction)
    {
        case DIR_RIGHT:
            x = map->width;
            while (b && x--)
                for (y = 0; b && y < map->height; y++)
                    b &= (map->state[y * map->width + x] == MAP_STATE_FREE);
            return map->width - 1 - x;
        case DIR_DOWN:
            y = map->height;
            while (b && y--)
                for (x = 0; b && x < map->width; x++)
                    b &= (map->state[y * map->width + x] == MAP_STATE_FREE);
            return map->height - 1 - y;
        case DIR_LEFT:
            x = -1;
            while (b && x++ < map->width)
                for (y = 0; b && y < map->height; y++)
                    b &= (map->state[y * map->width + x] == MAP_STATE_FREE);
            return x;
        case DIR_UP:
            y = -1;
            while (b && y++ < map->height)
                for (x = 0; b && x < map->width; x++)
                    b &= (map->state[y * map->width + x] == MAP_STATE_FREE);
            return y;
    }
}

void ResizeMap(Map* map, int lDiff, int tDiff, int rDiff, int bDiff)
{
    int x, y, i;
    char* oldData = map->data;
    char* oldState = map->state;

    Lock(mapMutex);

    map->width += lDiff + rDiff;
    map->height += tDiff + bDiff;
    map->data = (char*)malloc(map->width * map->height);
    map->state = (char*)malloc(map->width * map->height);

    for (y = 0; y < map->height; y++)
        for (x = 0; x < map->width; x++)
            if (x >= lDiff && x < map->width - rDiff && y >= tDiff && y < map->height - bDiff)
            {
                map->data[y * map->width + x] = oldData[(y - tDiff) * (map->width - lDiff - rDiff) + (x - lDiff)];
                map->state[y * map->width + x] = oldState[(y - tDiff) * (map->width - lDiff - rDiff) + (x - lDiff)];
            }
            else
            {
                map->data[y * map->width + x] = 0x30;
                map->state[y * map->width + x] = MAP_STATE_FREE;
            }

    Lock(workerMutex);
    for (i = 0; i < workerCount; i++)
    {
        workerList[i]->destiny.X = workerList[i]->position.X += lDiff;
        workerList[i]->destiny.Y = workerList[i]->position.Y += tDiff;
    }
    UnLock(workerMutex);

    Lock(objectMutex);
    for (i = 0; i < objectCount; i++)
    {
        objectList[i]->position.X += lDiff;
        objectList[i]->position.Y += tDiff;
    }
    UnLock(objectMutex);

    UnLock(mapMutex);

    free(oldData);
    free(oldState);
    GenerateMapBmp(map);
}
